#ifndef GAME_ANIMATION_GRID_GRID_LOCATOR_H
#define GAME_ANIMATION_GRID_GRID_LOCATOR_H
//
// Game Animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <animation/configuration.h>

#include <cassert>

namespace animation
{

  /**
  *  A virtual grid is a 2D dynamic storage for cells.
  *
  *  The idea is to keep a sqaure neighbourhood of cells
  *  around the current avatar (i.e. camera) position.
  *
  *  It is assumed that the band of cells around the avatar
  *  have equal size everywhere, thus we requre the number
  *  of cells along the x- and y-axes to be odd numbers.
  *
  *  Thus we have a local indexing (s,t) of cells and a
  *  global indexing (i,j) of cells
  *
  *       +-----+-----+-----+-----+-----+
  *       |     |     |     |     |     |
  *       +-----+-----+-----+-----+-----+
  *       |     |     |     |     |     |
  *       +-----+-----+-----+-----+-----+
  *  J=5  |     |     |(s,t)|     |     |
  *       +-----+-----+-----+-----+-----+
  *       |     |     |     |     |     | dy
  *       +-----+-----+-----+-----+-----+
  *       |(0,0)|     |     |     |     |
  *       +-----+-----+-----+--dx-+-----+
  *
  *                    I=5
  *    
  * Let avatar position be given by p=(x,y,z) then global
  * cell index containing avatar is 
  *
  *     i = float( x / dx)
  *     j = float( y / dy)
  *
  * The local index is given by (s,t). Given relative
  * indices (r,v) of some cell wrt. the cell containing
  * the avatar, then
  *
  *     local idx:   ( s+r, t+v )
  *     global idx:  ( i+r, j+v )
  *
  * When moving avatar from one cell to another, the virtual
  * grid needs to be updated in some fashion. We choice a
  * cyclic storage approach for the grid.
  *
  * This effect the way we compute local indices given relative
  * indices of other cells
  *
  *     local idx:   ( (s+r)%I, (t+v)%J )
  *
  * It may very well be that the relative indices is too big to
  * fit inside the virtual grid. This is the case if:
  *
  *     r > floor(I/2) or r < -floor(I/2)
  *
  * The test for the s-index is similar.
  *
  *
  * A cell center position can be defined as well, given global
  * indices (i,j) of a cell, the center (cx,cy) is
  *
  *    cx  =  i*dx + dx/2
  *    cy  =  j*dy + dy/2
  *
  *
  */
  template<typename types>
  class GridLocator 
  {
  public:

    typedef typename types::index_type    index_type;
    typedef typename types::real_type     real_type;

  protected:

    index_type    m_I;          ///< Number of cells along x-axis in virtual grid.
    index_type    m_J;          ///< Number of cells along y-axis in virtual grid.
    index_type    m_s;          ///<  local grid indices of cell containing avatar.
    index_type    m_t;
    index_type    m_i;           ///<  global grid indices of cell containing avatar.
    index_type    m_j;
    real_type     m_dx;         ///< Cell extent along x-axis.
    real_type     m_dy;         ///< Cell extent along y-axis.

  public:

    index_type const & I()const {return m_I;}
    index_type const & J()const {return m_J;}

    index_type const & s()const {return m_s;}
    index_type const & t()const {return m_t;}
    index_type const & i()const {return m_i;}
    index_type const & j()const {return m_j;}

    index_type & s() {return m_s;}
    index_type & t() {return m_t;}
    index_type & i() {return m_i;}
    index_type & j() {return m_j;}

    real_type const & dx()const {return m_dx;}
    real_type const & dy()const {return m_dy;}

  public:

    GridLocator()
      : m_I(0)
      , m_J(0)
      , m_s(0)
      , m_t(0)
      , m_i(0)
      , m_j(0)
      , m_dx(0)
      , m_dy(0)
    { }

  public:

    /**
     * Initialization.
     *
     * @param I                 Number of grid cells along x-axis.
     * @param J                 Number of grid cells along y-axis.
     * @param dx                The length of the grid cells along the x-axis.
     * @param dy                The length of the grid cells along the y-axis.
     */
    void init(index_type const & I,index_type const & J,real_type const & dx,real_type const & dy)
    {
      assert((I%2==1) || !"GridLocator.init(): I must be a odd number");
      assert((J%2==1) || !"GridLocator::init(): J must be a odd number");
      assert(dx>0 || !"GridLocator::init(): dx must be positive");
      assert(dy>0 || !"GridLocator::init(): dy must be positive");
      m_I = I;
      m_J = J;
      m_dx = dx;
      m_dy = dy;

      //--- initialize avatar to be at center cell of virtual grid
      m_s = m_I/2;
      m_t = m_J/2;
      //--- and at origin of the world!!!
      m_i = 0;
      m_j = 0;
    }

  public:

    //--- Methods for handling local indices

    index_type get_s(index_type const & r)const  
    {  
      assert( r <= m_I/2 || !"GridLocator()::get_s(): r was our of bounds");
      index_type s = (m_s + r)%m_I;
      if(s<0)
        s += m_I;
      return s; 
    }

    index_type get_t(index_type const & v)const 
    {  
      assert( v <= m_J/2 || !"GridLocator()::get_t(): v was our of bounds");
      index_type t = (m_t + v)%m_J;
      if(t<0)
        t += m_J;
      return t; 
    }

  public:

    //--- Methods for handling global indices

    index_type get_i(index_type const & r)const  { return m_i + r;  }
    index_type get_j(index_type const & v)const {  return m_j + v;  }

    index_type get_i(real_type const & x) const { return  static_cast<index_type>( std::floor( (x / m_dx) +.5 ) ); }
    index_type get_j(real_type const & y) const { return  static_cast<index_type>( std::floor( (y / m_dy) +.5 ) ); }

  public:

    //--- Methods for handling cell positions and corners

    real_type get_cell_center_x(index_type const & i) const { return i*m_dx; };
    real_type get_cell_center_y(index_type const & j) const { return j*m_dy; };
    real_type get_cell_lower_x(index_type const & i) const { return (i-.5)*m_dx; };
    real_type get_cell_lower_y(index_type const & j) const { return (j-.5)*m_dy; };
    real_type get_cell_upper_x(index_type const & i) const { return (i+.5)*m_dx; };
    real_type get_cell_upper_y(index_type const & j) const { return (j+.5)*m_dy; };

  };

} // namespace animation

// GAME_ANIMATION_GRID_GRID_LOCATOR_H
#endif
